﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

//函数的递归
//递归其实是⼀种解决问题的方法，在C语言中，递归就是函数自己调用自己


//史上最简单的递归代码
//这是一个错误的示范，可能会导致栈溢出
//int main()
//{
//	printf("hehe\n");
//	main();
//
//	return 0;
//}


//如果递归无限的递归下去，就会出现栈溢出
//每一次函数调用，都要为这次函数调用分配内存空间，是从内存的栈区上分配的
//如果无限的递归调用函数，就会将栈区空间填满（使用完），这时就会栈溢出


//把⼀个大型复杂问题层层转化为⼀个与原问题相似，但规模较⼩的子问题来求解；直到子问题不能再被拆分，递归就结束了
//所以递归的思考方式就是把⼤事化⼩的过程。
//递归中的递就是递推的意思，归就是回归的意思

//递归在书写的时候，有2个必要条件：
//1.递归存在限制条件，当满足这个限制条件的时候，递归便不再继续。
//2.每次递归调⽤之后越来越接近这个限制条件



//⼀个正整数的阶乘（factorial）是所有⼩于及等于该数的正整数的积，并且0的阶乘为1。⾃然数n的阶乘写作n!。
//题⽬：计算n的阶乘（不考虑溢出），n的阶乘就是1~n的数字累积相乘。
//int Fact(int n)
//{
//	if (n == 0)
//		return 1;
//	else if (n > 0)
//		return n * Fact(n - 1);
//}
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int ret = Fact(n);
//	printf("%d\n", ret);
//
//
//	return 0;
//}



//输入一个整数，按照顺序打印它的每一位
//void Print(int n)
//{
//	if (n > 9)
//		Print(n / 10);
//
//	printf("%d ", n % 10);
//}
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	Print(n);
//
//	return 0;
//}



//在C语⾔中每⼀次函数调⽤，都需要为本次函数调⽤在内存的栈区，申请⼀块内存空间来保存函数调
//⽤期间的各种局部变量的值，这块空间被称为运⾏时堆栈，或者函数栈帧。
//函数不返回，函数对应的栈帧空间就⼀直占⽤，所以如果函数调⽤中存在递归调⽤的话，每⼀次递归
//函数调⽤都会开辟属于⾃⼰的栈帧空间，直到函数递归不再继续，开始回归，才逐层释放栈帧空间。
//所以如果采⽤函数递归的⽅式完成代码，递归层次太深，就会浪费太多的栈帧空间，也可能引起栈溢
//出（stack overflow）的问题。


//递归与迭代

//所以如果不想使⽤递归，就得想其他的办法，通常就是迭代的⽅式（通常就是循环的⽅式）。
//循环能够实现迭代，但迭代不一定能够实现循环



////n的阶乘的另一种写法
//int Fact(int n)
//{
//	int i = 0;
//	int ret = 1;
//	for (i = 1; i <= n; i++)
//	{
//		ret *= i;
//	}
//	return ret;
//}
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int r = Fact(n);
//	printf("%d\n", r);
//
//	return 0;
//}



//举例3：求第n个斐波那契数
//1 1 2 3 5 8 13 21 34 55.....
//公式：n<=2       1
//      n>2        Fib(n-1)+Fib(n-2)

//递归方式      效率低（不太合适）
//int count = 0;
//
//int Fib(int n)
//{
//	if (n == 3)
//	{
//		count++;
//	}
//	if (n <= 2)
//		return 1;
//	else
//		return Fib(n - 1) + Fib(n - 2);
//}
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int r = Fib(n);
//	printf("%d\n", r);
//	printf("count = %d\n", count);
//}




////迭代方式
//int Fib(int n)
//{
//	int a = 1;
//	int b = 1;
//	int c = 1;
//
//	while (n > 2)
//	{
//		c = a + b;
//		a = b;
//		b = c;
//		n--;
//	}
//	return c;
//}
//
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int r = Fib(n);
//	printf("%d\n", r);
//
//	return 0;
//}


//void test(int n)
//{
//	printf("%d ", n);
//	if (n <= 10000)
//	{
//		test(n + 1);
//	}
//}
//
//int main()
//{
//	test(1);
//
//	return 0;
//}











//操作符讲解


//操作符的分类
//• 算术操作符： +  -  *  /  %
//• 移位操作符 : << >> 
//• 位操作符 :  & |  ^  =
//• 赋值操作符 :  =  +=  -=  *=  /=  %=  <<=  >>=  &=  |=  ^= 
//• 单⽬操作符： ！ ++  --  &   *   +   -   ~   sizeof (类型)
//• 关系操作符 :  >   >=   <   <=   ==   != 
//• 逻辑操作符： &&   ||
//• 条件操作符： ?  :
//• 逗号表达式：,
//• 下标引⽤：[]
//• 函数调⽤：()
//• 结构成员访问：.  ->


//int main()
//{
//	//八进制
//	//如：2进制的01101011，换成8进制：0153，0开头的数字，会被当做8进制。
//	printf("%d\n", 153);
//	printf("%d\n", 0153);//前面加个0默认转为8进制数字
//
//
//
//	//十六进制
//	//如：2进制的01101011，换成16进制：0x6b，16进制表⽰的时候前⾯加0x
//	printf("%d\n", 0xa);
//
//	return 0;
//}




//整数的2进制表示方法有三种，即原码、反码和补码
//有符号整数的三种表示方法均有符号位和数值位两部分，2进制序列中，最⾼位的1位是被当做符号位，剩余的都是数值位。
//符号位都是⽤0表⽰“正”，⽤1表⽰“负”。

//正整数的原、反、补码都相同。
//负整数的三种表示方法各不相同。

//原码：直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
//反码：将原码的符号位不变，其他位依次按位取反就可以得到反码
//补码：反码 + 1就得到补码。



//int main()
//{
//	int a = 10;
//	//000000000000000000000000000000000    - 源码
//	//000000000000000000000000000000000    - 反码
//	//000000000000000000000000000000000    - 补码
//
//	int b = -10;
//	//100000000000000000000000000001010    - 源码
//	//111111111111111111111111111110101    - 反码
//	//111111111111111111111111111110110    - 补码
//
//	//补码得到源码：也可以取反 在加1
//
//	return 0;
//}

//对于整形来说：数据存放内存中其实存放的是补码。
//在计算机系统中，数值⼀律⽤补码来表⽰和存储。原因在于，使⽤补码，可以将符号位和数值域统⼀处理；
//同时，加法和减法也可以统⼀处理（CPU只有加法器）此外，补码与原码相互转换，其运算过程是相同的，不需要额外的硬件电路。



int main()
{
	//1 - 1;
	//1 + (-1);
	//尝试用源码计算
	//000000000000000000000000000000001       1的源码
	//100000000000000000000000000000001       -1的源码
	//100000000000000000000000000000010    -2 //err


	//尝试用补码计算
	//000000000000000000000000000000001       1的补码
	//111111111111111111111111111111111       -1的补码
	//100000000000000000000000000000000      0

	return 0;
}
